/*----------------------------------------------------------------------------
 *      MODUL KONTROL UTAMA SISTEM KAMUFLASE TEMPUR
 *----------------------------------------------------------------------------
 *      Name:    MAIN.C
 *      Algoritma modul kontrol utama sistem kamuflase tempur
 *---------------------------------------------------------------------------*/

/*------------------
 *    INCLUDES
 *------------------*/
#include <inttypes.h>
#include <RTL.h>

#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_usart.h"
#include "misc.h"


#include "SWV_debug.h"
#include "sysConfig.h"
#include "bufferQue.h"
#include "myUSART.h"
#include "my485.h"
#include "LED.h"
#include "que.h"
#include "command.h"
#include "commandController.h"

#include "uiCommand.h"

#include "button.h"


/*------------------
 *    GLOBAL
 *------------------*/
int systemActivated;
int systemCamoMode;

extern bufferQue bufferCOM[3];

bufferQue UICmdBuff;

extern int N_NETWORK;
extern int N_PLATE;
extern int N_PAIR;
extern int N_TEMPLATE;

T_plateList PlateList;
T_plateMap PlateMap; 
T_pairList PairList;
T_templateList TemplateList;
T_plateDB plateDB;

/*------------------
 *    TEMP
 *------------------*/
extern USART_TypeDef* COM[3];
extern que    quePltCmd[2];
 

#define JPT0 0
#define JPT1 1
#define UI 2
 
 /*--------------*/
 
 

 /*--------------*/


 void simpleDelay(uint32_t ms) {
    ms *= 3360;
    while(ms--) {
        __NOP();
    }
}

 
void setup (void){
    int x=0;
    SWV_puts("--   START INIT\n");


    SWV_puts("   - USART\n");
    initALL_USART();
     
    SWV_puts("   - GPIO LED\n");
    LEDInit();
     
    SWV_puts("   - GPIO 485\n");
    init485();
     
    SWV_puts("   - DATASET\n");
    initDataSet();
     
	
	initPlateList(&PlateList);
    initPlateMap(&PlateMap);
    initPairList(&PairList);
	initTemplateList(&TemplateList);
    initPlateDB (&plateDB);

     
    makeEmpty(&quePltCmd[0]);
    makeEmpty(&quePltCmd[1]);
    
    bMakeEmpty( &UICmdBuff );
    
    systemCamoMode = 0;
    systemActivated = 0;
    
    //initButton();
    
    while (++x < 10){
        LEDOn(x%4);
        simpleDelay(1000);
        LEDOffAll();
        simpleDelay(1000);
    }
    
    SWV_puts(" \n--  END INIT\n\n");
    
}

void updateButton(){
    uint8_t currentButtonStatus = GPIO_ReadInputDataBit(GPIOA, USER_BUTTON);
    static int lastButtonStatus = !RESET;

    SWV_puts("\n--but: ");
    SWV_printnum(currentButtonStatus);
    if (lastButtonStatus != currentButtonStatus && currentButtonStatus != RESET) {
        if (systemActivated != 0) {
            systemCamoMode = systemCamoMode + 1;}
        else {
            systemActivated = 1;
            systemCamoMode = 0;
        }        
        if (systemCamoMode == 3) {
            systemCamoMode = 0;
            systemActivated = 0;
        }
        
        
    }
    lastButtonStatus = currentButtonStatus;

}


void updateLED(){
    static int lastSystemCamoMode = -99;
    if (systemActivated == 0){
        LEDOffAll();
    } else {
        if (lastSystemCamoMode != systemCamoMode){
            LEDOffAll();
            LEDOn(systemCamoMode);
            lastSystemCamoMode = systemCamoMode;
        }
    }
}


#define MAX_TRY_COUNT 4
#define MAX_TIMEOUT_COUNT 4000

void updateLoop (int netw){
    command cmd;
    int COMx;
    int absis, ordinat, target;
    
    int nPlateinNetw;
    int plateNum;
    int plateID;
    T_plateData PD, PDTemp;
    
    int replyStatus;
    int replyOK;
    int tryCount;
    int mTryCount;
    
    int availablePlates;
    int goodReply;
    
    uint32_t timeOutCount;
    
    COMx = netw;
     
    availablePlates = 0;
    goodReply = 0;
    
    nPlateinNetw = getNPlateinNetw(PlateMap ,netw );
    for (plateNum = 0; plateNum < nPlateinNetw; ++plateNum){
        
        
        
        plateID = getIDnInNetw(PlateMap ,netw ,plateNum );
        PD = getPlateData( plateDB, plateID);
        
        if (PD.condition) {
            tryCount = MAX_TRY_COUNT;
            mTryCount = tryCount;
            ++availablePlates;
        } else {
            //COBA 1 kali saja jika kondisi plat ambigu
            tryCount = 1;
            mTryCount = tryCount;
        }
            
        replyOK = 0;
        absis = getAbs(PlateList,plateID); 
        ordinat = getOrd(PlateList,plateID); 
        target = 0;
        
//         SWV_puts("systemActivated : ( ");
//         SWV_printnum(systemActivated);
//         SWV_puts(" systemCamoMode ");
//         SWV_printnum(systemCamoMode);
//         SWV_puts(" \n ");
        
        // TODO sesuaikan targetTemp dengan mode -- DONE
        if (systemActivated) {
            if (systemCamoMode == 0){
                //MODE MENGHILANG
                PDTemp = getPlateData(plateDB, getPairID ( PairList, plateID ) );  
                target = PDTemp.BGTemp;
                
            } else if (systemCamoMode > 0){
                //MODE MENYAMAR
                target = getTemplateID(TemplateList, plateID, systemCamoMode-1);
                
            }
        } else {
            // systemActivated == 0
            // Sistem Tidak Aktif query dengan target 0
            target = 0;
        }
        
        
        PD.targetTemp = target;
        
        SWV_puts("QUERYING : ( ");
        SWV_printnum(absis);
        SWV_puts(" , ");
        SWV_printnum(ordinat);
        SWV_puts(" : ");
        SWV_printnum(target);
        SWV_puts(" ) ");
            
        while (!replyOK && tryCount) {
            replyOK = 0;
            --tryCount;
            SWV_puts(" TRIAL#");
            SWV_printnum(mTryCount-tryCount);
            SWV_puts(" ");
            
        
            cmd = makeCommand(SETTEMP_CMD, absis , ordinat , target);
            sendCommand( cmd, COMx);                
            bMakeEmpty(&bufferCOM[COMx]);
            
            replyStatus = NO_REPLY;
            
            // TODO tambahkan  kondisi timeout disini  -- DONE
            timeOutCount = MAX_TIMEOUT_COUNT;
            
            while ( (replyStatus == NO_REPLY) 
                     && (timeOutCount>0)
                  ) {
                replyStatus = getCommand(COMx);
                timeOutCount--;
            }
            
//          SWV_puts("::::   timeOutCount: ");
//          SWV_printnum(MAX_TIMEOUT_COUNT-timeOutCount);
//          SWV_puts("\n");
            
            if (replyStatus != NO_REPLY){
                cmd = parseQue(quePltCmd[COMx]);  
                switch (cmd.commandType) {
                    case REPTEMP_CMD:
                        SWV_puts(" OK \n");
                        SWV_puts("GOT:: REPTEMP_CMD ");
                        SWV_puts(" :ACTEMP: ");
                        SWV_printnum(cmd.value[0]);
                        SWV_puts(" ");
                        SWV_puts(" :BGTEMP: ");
                        SWV_printnum(cmd.value[1]);
                        SWV_puts("\n");
                    
                         ++goodReply;
                    
                        PD.ActTemp = cmd.value[0];
                        PD.BGTemp = cmd.value[1];
                        PD.condition = 1;      
                    
                        setPlateData(&plateDB, plateID, PD);   
                        
                        replyOK = 1;
                    
                        bMakeEmpty(&bufferCOM[COMx]);
                    break;
                    case BROKEN_CMD:
//                         SWV_puts("GOT:: BROKEN_CMD \n");
//                         //tryCount = 0;                         
                    break;
                    case FALSECRC_CMD:
//                         SWV_puts("GOT:: FALSECRC_CMD \n");
                    break;        

                }
            } else {

//              SWV_puts("GOT:: TIMEOUT \n");
                //tryCount = 0;
                // TODO update kondisi timeout disini  -- DONE
            }
        }
        
        if (tryCount == 0 && !replyOK){
            SWV_puts(" FAIL \n");
            PD.condition = 0;
            setPlateData(&plateDB, plateID, PD);
        }
           
    }
    

    SWV_puts("RESULT QUERYING JPT#");
    SWV_printnum(netw);        
    SWV_puts("  avaDB:  ");
    SWV_printnum(availablePlates);
    SWV_puts("  goodRep:  ");
    SWV_printnum(goodReply);
    SWV_puts("\n");

       
}



void interfaceUILoop(){
    uint8_t tempRead;
    int haveReply;
    uiCommand uicmd;
    uiCommand uicmdRep;
    
    uint8_t absis, ordinat;    
    uint8_t temp1, temp2;
    uint8_t tempOut1;
    bufferQue bqRep;
    uint8_t tempout;
    
    T_plateData PDTemp;
    uint8_t plateIDGlobal;
    
    
    //SWV_puts(" interfaceUILoop \n");
    while ( bLen ( bufferCOM[UI ] ) > 0) {
        tempRead = bDeque( &bufferCOM[UI ] );
        bEnque( &UICmdBuff, tempRead);
        if (tempRead == UICMDDELIMITER) {
            SWV_puts(": got UICMDDELIMITER \n");
            uicmd = parseBQue (UICmdBuff);
            
            haveReply = 1;
            
            switch (uicmd.type) {
                case TEST_Q :
                    SWV_puts(":: got TEST_Q \n");
                    uicmdRep = makeUICMD (OK_A, 0, 0, 0, 0, 0, 0);
                break;
                case NNETW_Q :
                    SWV_puts(":: got NNETW_Q \n");
                    uicmdRep = makeUICMD (NNETW_A, N_NETWORK, 0, 0, 0, 0, 0);
                break;
                case SYST_ON :
                    SWV_puts(":: got SYST_ON \n");
                    SWV_puts(":: Camo System Activated\n");
                    systemActivated = 1;
                    uicmdRep = makeUICMD (OK_A, 0, 0, 0, 0, 0, 0);
                break;
                case SYST_OFF :
                    SWV_puts(":: got SYST_OFF \n");
                    SWV_puts(":: Camo System deActivated\n");
                    systemActivated = 0;
                    uicmdRep = makeUICMD (OK_A, 0, 0, 0, 0, 0, 0);
                break;
                case NETW_Q :
                    SWV_puts(":: got NETW_Q \n");
                    // NetworkID 
                    temp1 = uicmd.value[0];
                    
                    // jumlah plat dalam network NetworkID
                    tempOut1 =  getNPlateinNetw(PlateMap ,temp1 );
                
                    uicmdRep = makeUICMD ( NETW_A, temp1, tempOut1, 0, 0, 0, 0);
                break;
                case PDATA_Q :
                    SWV_puts(":: got PDATA_Q \n");
                    // NetworkID 
                    temp1 = uicmd.value[0];
                    // plateID# in Netw. NetworkID 
                    temp2 = uicmd.value[1];
                    
                    plateIDGlobal = getIDnInNetw(PlateMap ,temp1 ,temp2 );
                    PDTemp = getPlateData( plateDB, plateIDGlobal);
                    
                    absis = getAbs(PlateList, plateIDGlobal);
                    ordinat = getOrd(PlateList, plateIDGlobal);
                
                    uicmdRep = makeUICMD (  PDATA_A,
                                            absis,
                                            ordinat,
                                            PDTemp.condition,
                                            PDTemp.targetTemp,
                                            PDTemp.ActTemp,
                                            PDTemp.BGTemp);
                break;
                case SETMODE_Q :
                    SWV_puts(":: got SETMODE_Q \n");
                    //targetMode
                    temp1 = uicmd.value[0];
                
                    if (temp1 > 200) {
                        tempOut1 = systemCamoMode;
                    } else {
                        systemCamoMode = temp1;
                        tempOut1 = systemCamoMode;
                    }
                    
                    uicmdRep = makeUICMD (SETMODE_A, tempOut1, 0, 0, 0, 0, 0);
                break;
                case GETNMODE_Q :
                    SWV_puts(":: got GETNMODE_Q \n");
                    tempOut1 = N_TEMPLATE;
                    uicmdRep = makeUICMD (GETNMODE_A, tempOut1, 0, 0, 0, 0, 0);
                break;
                default: 
                    haveReply = 0;
                break;
            }
            
            if (haveReply){
                bqRep = makeBQue ( uicmdRep );
                while (bLen ( bqRep ) > 0) {
                    tempout = bDeque ( &bqRep );
                    USART_putchar(COM[UI], tempout);                    
                }
            }
            
            bMakeEmpty(&UICmdBuff);
        }   
    }
}

/*------------------
 *    MAIN
 *------------------*/
int main(void)
{   
   
    setup();
    
    //systemActivated = 0;
    //systemCamoMode = 0;
    //os_sys_init(procInit);
    

    while(1) { 
        
        updateButton();
        updateLED();
        updateLoop(JPT1);        
        updateLoop(JPT0);
        interfaceUILoop();
        
        
    }
    
    // YOU SHALL NEVER RETURN
//    while(1){};
}



